package com.hoshiibuy.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;

/**
 * @author zhangshuhu
 * @title: BeanUtils
 * @projectName hoshiibuy-brandcategory
 * @description: 实体操作和转换工具类（扩展org.springframework.beans.BeanUtils）
 * @date 2019-04-23
 */
public class BeanUtils extends org.springframework.beans.BeanUtils {

	/**
	 * 储存反射类表查询字段信息
	 */
	private static final Map<Class<?>, String> TABLE_INFO_CACHE = new ConcurrentHashMap<>();
	/**
	 * 储存bean对象属性列表
	 */
	private static final Map<Class<?>, Map<String, Field>> BEAN_FIELD_CACHE = new ConcurrentHashMap<>();



	/**
	 * <p>
	 * 获取实体映射表查询字段信息
	 * <p>
	 * 
	 * @author Mr.wu
	 * @param clazz 反射实体类
	 * @return 数据库表反射查询字段信息 例:"id,name,type"
	 * @throws Exception
	 */
	public static String getSqlSelect(Class<?> clazz) throws Exception {
		if (clazz == null) {
			return null;
		}
		String sqlSelect = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(clazz));
		if (StringUtils.isNotBlank(sqlSelect)) {
			return sqlSelect;
		}
		// 尝试获取父类缓存
		Class currentClass = clazz;
		while (StringUtils.isBlank(sqlSelect) && Object.class != currentClass) {
			currentClass = currentClass.getSuperclass();
			sqlSelect = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(currentClass));
		}
		if (sqlSelect != null) {
			TABLE_INFO_CACHE.put(ClassUtils.getUserClass(clazz), sqlSelect);
			return sqlSelect;
		} else {
			// 根据字段tablefield注解获取数据库反射信息组成的查询字段
			sqlSelect = getSqlSelectOfClass(clazz);
			if (StringUtils.isNotBlank(sqlSelect)) {
				TABLE_INFO_CACHE.put(ClassUtils.getUserClass(clazz), sqlSelect);
				return sqlSelect;
			} else {
				return null;
			}
		}
	}

	/**
	 * 
	 * @Description: 根据tablefield注解获取数据库反射信息，组成所需查询的字段
	 * @author Mr.wu
	 * @param cls
	 * @return
	 * @throws Exception
	 * @date: 2019年4月26日 下午11:38:32
	 */
	private static String getSqlSelectOfClass(Class<?> cls) throws Exception {
		Class<?> itr = cls;
		StringBuilder stringBuilder = new StringBuilder();
		while ((null != itr) && !itr.equals(Object.class)) {
			Field[] declaredFields = itr.getDeclaredFields();
			if (declaredFields != null && declaredFields.length > 0) {
				for (Field f : declaredFields) {
					String fieldName = f.getName();

					if (!"serialVersionUID".equals(fieldName) && !Modifier.isStatic(f.getModifiers())) {
						TableField tableField = f.getAnnotation(TableField.class);
						if (tableField != null && tableField.select() && StringUtils.isNotBlank(tableField.value())) {
							stringBuilder.append(tableField.value());
							stringBuilder.append(",");
						} else {
							TableId tableId = f.getAnnotation(TableId.class);
							if (tableId != null && StringUtils.isNotBlank(tableId.value())) {
								stringBuilder.append(tableId.value());
								stringBuilder.append(",");
							}
						}
					}
				}
			}
			itr = itr.getSuperclass();
		}
		if (stringBuilder.length() > 0) {
			// 去掉最后的逗号
			stringBuilder.deleteCharAt(stringBuilder.length() - 1);
		}
		return stringBuilder.toString();
	}

	/**
	 * 属性copy和转换 兼容 lombok注解的属性
	 * 
	 * @param t
	 * @param clazz
	 * @param <T>
	 * @param <R>
	 * @return
	 * @throws Exception
	 */
	public static <T, R> R covertType(T t, Class<R> clazz) throws Exception {
		if (t == null)
			return null;
		Map<String, Field> fromFieldMap = getAllFieldsOfClass(t.getClass());
		R target = clazz.newInstance();
		Map<String, Field> toFieldMap = getAllFieldsOfClass(clazz);
		for (Map.Entry<String, Field> en : toFieldMap.entrySet()) {
			String fieldName = en.getKey();
			Field valueField = fromFieldMap.get(fieldName);
			if (valueField != null) {
				Object val = valueField.get(t);
				if (val != null && !"".equals(val)) {
					en.getValue().set(target, val);
				}
			}
		}
		return target;
	}

	private static Map<String, Field> getAllFieldsOfClass(Class<?> cls) {
		Map<String, Field> fieldmap = BEAN_FIELD_CACHE.get(ClassUtils.getUserClass(cls));
		if(fieldmap==null){
			fieldmap = new HashMap();
			Class<?> itr = cls;
			while ((null != itr) && !itr.equals(Object.class)) {
				Field[] declaredFields = itr.getDeclaredFields();
				if (declaredFields != null && declaredFields.length > 0) {
					for (Field f : declaredFields) {
						String fieldName = f.getName();
						if (fieldmap.get(fieldName) == null && !"serialVersionUID".equals(fieldName)
								&& !Modifier.isStatic(f.getModifiers())) {
							fieldmap.put(fieldName, f);
							f.setAccessible(true);
						}
					}
				}
				itr = itr.getSuperclass();
			}
			BEAN_FIELD_CACHE.put(ClassUtils.getUserClass(cls),fieldmap);
			return fieldmap;
		}else{
			return fieldmap;
		}

	}
}
